
package com.x52im.rbchat.network.im;

import android.content.Context;
import android.util.Log;

import com.eva.epc.common.util.CommonUtils;
import com.x52im.rbchat.IMClientManager;
import com.x52im.rbchat.MyApplication;
import com.x52im.rbchat.R;
import com.x52im.rainbowchat.http.logic.dto.GroupEntity;
import com.x52im.rainbowchat.http.logic.dto.RosterElementEntity;
import com.x52im.rainbowchat.im.dto.CMDBody4GroupNameChangedNotification;
import com.x52im.rainbowchat.im.dto.CMDBody4MyselfBeInvitedGroupResponse;
import com.x52im.rainbowchat.im.dto.MsgBody4Friend;
import com.x52im.rainbowchat.im.dto.MsgBody4Group;
import com.x52im.rainbowchat.im.dto.MsgBody4Guest;
import com.x52im.rainbowchat.im.dto.MsgBodyRoot;
import com.x52im.rbchat.logic.chat_friend.gift.GiftsProvider;
import com.x52im.rbchat.logic.chat_friend.gift.model.Gift;
import com.x52im.rbchat.logic.chat_friend.gift.model.GiftInPackageMeta;
import com.x52im.rbchat.logic.chat_friend.gift.model.GiftsMeta;
import com.x52im.rbchat.logic.chat_friend.utils.ChatDataHelper;
import com.x52im.rbchat.logic.chat_friend.utils.MessageHelper;
import com.x52im.rbchat.logic.chat_friend.utils.RealTimeVoiceMessageHelper;
import com.x52im.rbchat.logic.chat_friend.vv.IVideoActivity;
import com.x52im.rbchat.logic.chat_guest.utils.TChatDataHelper;
import com.x52im.rbchat.logic.chat_guest.utils.TMessageHelper;
import com.x52im.rbchat.network.http.async.QueryOfflineChatMsgAsync;
import com.x52im.rbchat.utils.BroadcastToolKits;
import com.x52im.rbchat.utils.NotificationPromptHelper;
import com.x52im.rbchat.cache.GroupsProvider;
import com.x52im.rbchat.logic.chat_group.utils.GChatDataHelper;
import com.x52im.rbchat.logic.chat_group.utils.GMessageHelper;
import com.x52im.rbchat.utils.PromtHelper;

import java.util.Observer;

/**
 * 用于处理 ChatMessageEventListener 类中的一些具体指令逻辑的实用方法类。
 * 本类中的方法提取至此的目的，仅仅是为了让 ChatMessageEventListener 类中的代码量变少，提升代码可读性而已。
 *
 * @since 4.6
 */
public class ChatMessageEventProcessor {
    private final static String TAG = ChatMessageEventProcessor.class.getSimpleName();

    /**
     * 处理 processMT01_OF_ONLINE_NOTIVICATION 指令逻辑。
     *
     * @param context
     * @param msg
     */
    public static void processMT01(Context context, String msg){
        // 服务端发来的上线通知
        String uid = MessageHelper.pareseRecieveOnlineNotivication(msg);

        // 设置上线状态
        RosterElementEntity friendRee = MyApplication.getInstance2().getIMClientManager().getFriendsListProvider().getFriendInfoByUid2(uid);
        if(friendRee != null)
            friendRee.online();

        // 好友上线了就尝试获取该用户可能发过来的离线消息（此时离线消息可能会在网络情况复杂的情况下发生（比如对方在发时我被判定不在线，但实际我是在线的等等））
        new QueryOfflineChatMsgAsync(context).execute(uid);
    }

    /**
     * 处理 processMT02_OF_OFFLINE_NOTIVICATION 指令逻辑。
     *
     * @param msg
     */
    public static void processMT02(String msg){
        String uid = MessageHelper.pareseRecieveOfflineNotivication(msg);
        // 设置下线状态
        RosterElementEntity friendRee = MyApplication.getInstance2().getIMClientManager().getFriendsListProvider().getFriendInfoByUid2(uid);
        if(friendRee != null)
            friendRee.offline();
    }

    /**
     * 处理 processMT43_OF_TEMP$CHAT$MSG_SERVER$TO$B 指令逻辑。
     *
     * @param context
     * @param fingerPrintOfProtocal
     * @param msg
     */
    public static void processMT43(Context context, String fingerPrintOfProtocal, String msg){
        Log.d(TAG, TMessageHelper.parseTempChatMsg_SERVER$TO$B_Message(msg).toString());
        // 来自发送方的临时聊天消息
        MsgBody4Guest tcmd = TMessageHelper.parseTempChatMsg_SERVER$TO$B_Message(msg);
        // 将数据放入
        TChatDataHelper.addChatMessageData_incoming(context, fingerPrintOfProtocal, tcmd, 0, true, true, tcmd);
    }

    /**
     * 处理 processMT45_OF_GROUP$CHAT$MSG_SERVER$TO$B 指令逻辑。
     *
     * @param context
     * @param fingerPrintOfProtocal
     * @param msg
     */
    public static void processMT45(Context context, String fingerPrintOfProtocal, String msg) {
        //** 特别说明：因群聊消息是由服务端发的（不像一对一聊天消息是通过client to client消息模式），所以
        //**         此处的消息发送者user_id字段的值是服务器（即"0"），而非消息真正的源头用户id哦，但
        //**         Protocal的dataContent里MsgBody4Guest对象里的f字段才是真正的用户源id哦！

        IMClientManager imc = MyApplication.getInstance2().getIMClientManager();

        Log.d(TAG, GMessageHelper.parseGroupChatMsg_SERVER$TO$B_Message(msg).toString());

        // 来自发送方的群聊天消息
        MsgBody4Group msgBody = GMessageHelper.parseGroupChatMsg_SERVER$TO$B_Message(msg);

        // 在群聊消息时，本字段存放的是群id，普通一对的聊天时才是用户uid
        String toGid = msgBody.getT();

        // 找到源用户要发送到的群基本信息
        GroupEntity ge = null;
        // 如果是世界频道
        if (GroupEntity.isWorldChat(toGid))
            ge = GroupsProvider.getDefaultWordChatEntity();
            // 否则是普通群聊
        else
            ge = imc.getGroupsProvider().getGroupInfoByGid(toGid);

        if (ge != null) {
            // 将数据放入
            GChatDataHelper.addChatMessageData_incoming(context, fingerPrintOfProtocal, ge.getG_id(), ge.getG_name(), msgBody, 0, true, true, msgBody);
        } else {
            Log.w(TAG, "来自userid=" + msgBody.getF() + "的群聊消息虽收到，但目标群"
                    + toGid + "并不在我的群列表里，本条群消息将被忽略！！");
        }
    }

    /**
     * 处理 processMT46_OF_GROUP$SYSCMD_MYSELF$BE$INVITE_FROM$SERVER 指令逻辑。
     *
     * @param context
     * @param msg
     */
    public static void processMT46(Context context, String msg) {
        IMClientManager imc = MyApplication.getInstance2().getIMClientManager();

        CMDBody4MyselfBeInvitedGroupResponse cmdBody = GMessageHelper.parseResponse4GroupSysCMD4MyselfBeInvited(msg);
        Log.d(TAG, cmdBody != null ? cmdBody.toString() : null);
        // 群基本信息
        GroupEntity ge = cmdBody;
        if (ge != null) {
            // 将新加入的群信息加入到本地的群缓存列表中
            imc.getGroupsProvider().putGroup(ge);
            // 来一个系统通知哦
            NotificationPromptHelper.showMyselfBeInvitedGroupNotivication(context, ge.getG_name(), ge.getG_id());
            String hintTex = "\"" + cmdBody.getInitveBeNickName() + "\"邀请您加入了群聊";
            // 将该条系统通知加入到聊天消息中
            GChatDataHelper.addSystemInfoData(context, ge.getG_id(), ge.getG_name(), hintTex, 0, true, true);
        } else {
            Log.w(TAG, "来自gid=" + cmdBody.getG_id() + "的加群成功后通知，但ge==null，本条通知将被忽略！！");
        }
    }

    /**
     * 处理 processMT47_OF_GROUP$SYSCMD_COMMON$INFO_FROM$SERVER 指令逻辑。
     *
     * @param context
     * @param fromUid
     * @param msg
     */
    public static void processMT47(Context context, String fromUid, String msg) {
        IMClientManager imc = MyApplication.getInstance2().getIMClientManager();

        // 群的系统通知，本质还是个群聊消息体（用类型区分就可以了）
        MsgBody4Group cmdBody = GMessageHelper.parseGroupChatMsg_SERVER$TO$B_Message(msg);
        Log.d(TAG, cmdBody != null ? cmdBody.toString() : null);
        // 在群聊消息时，本字段存放的是群id，普通一对的聊天时才是用户uid
        String toGid = cmdBody.getT();
        // 找到源用户要发送到的群基本信息
        GroupEntity ge = null;
        // 如果是世界频道
        if (GroupEntity.isWorldChat(toGid))
            ge = GroupsProvider.getDefaultWordChatEntity();
            // 否则是普通群聊
        else
            ge = imc.getGroupsProvider().getGroupInfoByGid(toGid);

        if (ge != null) {
            // 将系统通知数据放入聊天界面
            GChatDataHelper.addChatMessageData_incoming(context, null, ge.getG_id(), ge.getG_name(), cmdBody, 0, true, true, cmdBody);
        } else {
            Log.w(TAG, "来自userid=" + fromUid + "的群聊系统MT47指令/通知虽收到，但目标群" + toGid + "并不在我的群列表里，本条群消息将被忽略！！");
        }
    }

    /**
     * 处理 processMT48_OF_GROUP$SYSCMD_DISMISSED_FROM$SERVER 指令逻辑。
     *
     * @param context
     * @param fromUid
     * @param msg
     */
    public static void processMT48_OF_GROUP$SYSCMD_DISMISSED_FROM$SERVER(Context context, String fromUid, String msg) {
        IMClientManager imc = MyApplication.getInstance2().getIMClientManager();

        // 群的系统通知，本质还是个群聊消息体（用类型区分就可以了）
        MsgBody4Group cmdBody = GMessageHelper.parseGroupChatMsg_SERVER$TO$B_Message(msg);
        Log.d(TAG, cmdBody != null ? cmdBody.toString() : null);
        // 在群聊消息时，本字段存放的是群id，普通一对的聊天时才是用户uid
        String toGid = cmdBody.getT();
        // 找到源用户要发送到的群基本信息
        GroupEntity ge = null;
        // 如果是世界频道
        if (GroupEntity.isWorldChat(toGid))
            ge = GroupsProvider.getDefaultWordChatEntity();
            // 否则是普通群聊
        else
            ge = imc.getGroupsProvider().getGroupInfoByGid(toGid);

        if (ge != null) {
            // 将系统通知数据放入聊天界面
            GChatDataHelper.addChatMessageData_incoming(context, null, ge.getG_id(), ge.getG_name(), cmdBody, 0, true, true, cmdBody);
            // 同时将此群基本信息从本地用户的群列表缓存中移除
            imc.getGroupsProvider().remove(ge.getG_id());
        } else {
            Log.w(TAG, "来自userid=" + fromUid + "的群聊系统MT48指令/通知虽收到，但目标群" + toGid + "并不在我的群列表里，本条群消息将被忽略！！");
        }
    }

    /**
     * 处理 MT49_OF_GROUP$SYSCMD_YOU$BE$KICKOUT_FROM$SERVER 指令逻辑。
     *
     * @param context
     * @param fromUid
     * @param msg
     */
    public static void processMT49(Context context, String fromUid, String msg) {
        IMClientManager imc = MyApplication.getInstance2().getIMClientManager();

        // 群的系统通知，本质还是个群聊消息体（用类型区分就可以了）
        MsgBody4Group cmdBody = GMessageHelper.parseGroupChatMsg_SERVER$TO$B_Message(msg);
        Log.d(TAG, cmdBody != null ? cmdBody.toString() : null);
        // 在群聊消息时，本字段存放的是群id，普通一对的聊天时才是用户uid
        String toGid = cmdBody.getT();
        // 找到源用户要发送到的群基本信息
        GroupEntity ge = null;
        // 如果是世界频道
        if (GroupEntity.isWorldChat(toGid))
            ge = GroupsProvider.getDefaultWordChatEntity();
            // 否则是普通群聊
        else
            ge = imc.getGroupsProvider().getGroupInfoByGid(toGid);

        if (ge != null) {
            // 将系统通知数据放入聊天界面
            GChatDataHelper.addChatMessageData_incoming(context, null, ge.getG_id(), ge.getG_name(), cmdBody, 0, true, true, cmdBody);
            // 同时将此群基本信息从本地用户的群列表缓存中移除
            MyApplication.getInstance(context).getIMClientManager().getGroupsProvider().remove(ge.getG_id());
        } else {
            Log.w(TAG, "来自userid=" + fromUid + "的群聊系统MT49指令/通知虽收到，但目标群" + toGid + "并不在我的群列表里，本条群消息将被忽略！！");
        }
    }

    /**
     * 处理 MT50_OF_GROUP$SYSCMD_SOMEONEB$REMOVED_FROM$SERVER 指令逻辑。
     *
     * @param context
     * @param fromUid
     * @param msg
     */
    public static void processMT50(Context context, String fromUid, String msg) {
        IMClientManager imc = MyApplication.getInstance2().getIMClientManager();

        // 群的系统通知，本质还是个群聊消息体（用类型区分就可以了）
        MsgBody4Group cmdBody = GMessageHelper.parseGroupChatMsg_SERVER$TO$B_Message(msg);
        Log.d(TAG, cmdBody != null ? cmdBody.toString() : null);
        // 在群聊消息时，本字段存放的是群id，普通一对的聊天时才是用户uid
        String toGid = cmdBody.getT();
        // 找到源用户要发送到的群基本信息
        GroupEntity ge = null;
        // 如果是世界频道
        if (GroupEntity.isWorldChat(toGid))
            ge = GroupsProvider.getDefaultWordChatEntity();
            // 否则是普通群聊
        else
            ge = imc.getGroupsProvider().getGroupInfoByGid(toGid);

        if (ge != null) {
            // 将系统通知数据放入聊天界面
            GChatDataHelper.addChatMessageData_incoming(context, null, ge.getG_id(), ge.getG_name(), cmdBody, 0, true, true, cmdBody);
            // 刷新群头像的更新时间戳，关于群头像的缓存更新逻辑，请见 GroupsProvider.refreshGroupImageUpdateTimestamp(..)方法的说明
            MyApplication.getInstance2().getIMClientManager().getGroupsProvider().refreshGroupImageUpdateTimestamp(ge);
        } else {
            Log.w(TAG, "来自userid=" + fromUid + "的群聊系统MT50指令/通知虽收到，但目标群" + toGid + "并不在我的群列表里，本条群消息将被忽略！！");
        }
    }

    /**
     * 处理 processMT51_OF_GROUP$SYSCMD_GROUP$NAME$CHANGED_FROM$SERVER 指令逻辑。
     *
     * @param context
     * @param msg
     */
    public static void processMT51(Context context, String msg) {
        IMClientManager imc = MyApplication.getInstance2().getIMClientManager();

        CMDBody4GroupNameChangedNotification cmdBody = GMessageHelper.parseResponse4GroupSysCMD4GroupNameChanged(msg);
        Log.d(TAG, cmdBody != null ? cmdBody.toString() : null);
        String toGid = cmdBody.getGid();
        GroupEntity ge = imc.getGroupsProvider().getGroupInfoByGid(toGid);
        if (ge != null) {
            // 更新缓存中的群名
            if (!CommonUtils.isStringEmpty(cmdBody.getNnewGroupName(), true))
                ge.setG_name(cmdBody.getNnewGroupName());

            String hintTex = cmdBody.getNotificationContent();
            // 将该条系统通知加入到聊天消息中
            GChatDataHelper.addSystemInfoData(context, ge.getG_id(), ge.getG_name(), hintTex, 0, true, true);

            // 通知ui界面进行刷新(当前主要是聊天界面)
            BroadcastToolKits.groupNameChanged_SEND(MyApplication.getInstance2(), ge.getG_id(), ge.getG_name());
        } else {
            Log.w(TAG, "来自gid=" + toGid + "的群名被改通知，但ge==null，本条通知将被忽略！！");
        }
    }

    /**
     * 处理 processMT07_OF_ADD_FRIEND_REQUEST_INFO_SERVER$TO$B 指令逻辑。
     *
     * @param context
     * @param msg
     */
    public static void processMT07(Context context, String msg){

        IMClientManager imc = MyApplication.getInstance2().getIMClientManager();

        RosterElementEntity srcUserInfo = MessageHelper.parseAddFriendRequestInfo_server$to$b(msg);
        Log.d(TAG, "收到了来自"+srcUserInfo.getNickname() +"("+srcUserInfo.getUser_uid()+")的加好友请求！！！");
        NotificationPromptHelper.showAddFriendRequestNotivication(context, srcUserInfo);

        // 自v4.3以后，本字段存放的是时间戳，而非人类可读的时间字串
        long reqTime = CommonUtils.getLongValue(srcUserInfo.getEx10());

        // 把提示消息放到列表的首位置
        imc.getAlarmsProvider().addAddFriendReqMergeAlarm(
                srcUserInfo.getUser_uid()
                , srcUserInfo.getNickname()
                , reqTime
                , 1 // 未读数字+1
                , true // 立即刷新ui显示
                , true);
        // 递增好友请求全局缓存中的总未读数
        imc.getFriendsReqProvider().incrementUnreadCount(true);
    }

    /**
     * 处理 processMT10_OF_PROCESS_ADD$FRIEND$REQ_FRIEND$INFO$SERVER$TO$CLIENT 指令逻辑。
     *
     * @param context
     * @param msg
     */
    public static void processMT10(Context context, String msg){
        IMClientManager imc = MyApplication.getInstance2().getIMClientManager();

        RosterElementEntity userInfoFromServer = MessageHelper.parseProcessAdd$Friend$Req_friend$Info$Server$To$ClientMessage(msg);
        String friendUid = userInfoFromServer.getUser_uid(), friendName = userInfoFromServer.getNickname();

        Log.d(TAG, "新好友"+userInfoFromServer.getNickname() +"("+userInfoFromServer.getUser_uid()+")已成功添加在好友列表中，可以聊天了！");

        // 将该好友加入到好友列表中
        imc.getFriendsListProvider().putFriend(userInfoFromServer);
        // 来一个声音提示
        PromtHelper.newFriendAddSucessPromt(context);
        // 来一个通知哦
        NotificationPromptHelper.showNewFriendAddSucessNotivication(context, friendName, friendUid);

        //## Bug FIX：20180111 by JackJiang
        //## > 未处理的好友请求数是显示在被请求方的界面里的，当被请求方“同意”好
        //## > 友请求时，会在同意完成时的逻辑里去将未读数-1，而不需要在这里（不然就重复减1了）
//				// 首页消息界面中加友请求alarms的“未读数”-1（成功加好友后，这个好友请求就以没意义了，未读数自然也要调整哦）
//				MyApplication.getInstance(context).getAlarmsProvider().accumulateAddFriendReqAlarmFlagNum(-1);
        //## Bug FIX：20180111 - END

        // 像微信等IM一样：被好加友同意加好友请求后，将入一条空消息到首页消息栏里，这样可以方便的点击此消息进入聊天界面
        imc.getAlarmsProvider().addChatMsgAlarmForAddSuccess(context, friendUid, friendName);
    }

    /**
     * 处理 processMT12_OF_PROCESS_ADD$FRIEND$REQ_SERVER$TO$A_REJECT_RESULT 指令逻辑。
     *
     * @param context
     * @param msg
     */
    public static void processMT12(Context context, String msg){
        IMClientManager imc = MyApplication.getInstance2().getIMClientManager();

        RosterElementEntity userInfoFromServer = MessageHelper.parseProcessAdd$Friend$Req_SERVER$TO$A_REJECT_RESULTMessage(msg);
        String friendUid = userInfoFromServer.getUser_uid(), friendName = userInfoFromServer.getNickname();

        Log.d(TAG, friendName+"("+friendUid+")拒绝了你的加好友请求哦！");

        // 来一个声音提示
        PromtHelper.tixintPromt(context);
        // 来一个通知哦
        NotificationPromptHelper.showAddFriendBeRejectNotivication(context, userInfoFromServer);
        // 加一条提示到主界面的提示功能列表中
        imc.getAlarmsProvider().addAddFriendBeRejectAlarm(friendUid, friendName);
//		// 清除指定好友曾今的好加友请求alarms（成功加好友后，这些alarms就没有意义了，可能包含该人员的多条重复请求，一并清除）
//		MyApplication.getInstance(context).getAlarmsProvider().removeAddFriendReqAlarm(userInfoFromServer.getF());
    }

    /**
     * 处理 processMT14_OF_VIDEO$VOICE_END$CHATTING 指令逻辑。
     *
     * @param context
     * @param msg
     */
    // TODO: 【考虑一下221116】参考实时视频和语音请求的处理方式，当APP处于后台时，是否需要给个系统Notification通知？
    public static void processMT14(Context context, String msg){
        IMClientManager imc = MyApplication.getInstance2().getIMClientManager();

        String friendUserUid = MessageHelper.pareseVideoAndVoice$EndChatting_from$a(msg);
        IVideoActivity currentVideoActivity = imc.getCurrentVideoChattingActivity();
        if(currentVideoActivity != null) {
            currentVideoActivity.showHint(context.getString(R.string.partnet_exit_talk), true);
            currentVideoActivity.finish();
        }
        else
            Log.d(TAG, "收到了来自"+friendUserUid+"的视频聊天：结束本次音视频聊天指令.");
    }

    /**
     * 处理 processMT15_OF_VIDEO$VOICE_SWITCH$TO$VOICE$ONLY 指令逻辑。
     *
     * @param context
     * @param msg
     */
    // TODO: 【考虑一下221116】参考实时视频和语音请求的处理方式，当APP处于后台时，是否需要给个系统Notification通知？
    public static void processMT15(Context context, String msg){
        IMClientManager imc = MyApplication.getInstance2().getIMClientManager();

        String friendUserUid = MessageHelper.pareseVideoAndVoice$SwitchToVoiceOnly_from$a(msg);
        IVideoActivity currentVideoActivity = imc.getCurrentVideoChattingActivity();
        if(currentVideoActivity != null) {
            currentVideoActivity.switchToVoiceOnly(true);
        } else
            Log.d(TAG, "收到了来自"+friendUserUid+"的视频聊天：切换到纯音频聊天模式.");
    }

    /**
     * 处理 processMT16_OF_VIDEO$VOICE_SWITCH$TO$VOICE$AND$VIDEO 指令逻辑。
     *
     * @param context
     * @param msg
     */
    // TODO: 【考虑一下221116】参考实时视频和语音请求的处理方式，当APP处于后台时，是否需要给个系统Notification通知？
    public static void processMT16(Context context, String msg){
        IMClientManager imc = MyApplication.getInstance2().getIMClientManager();

        String friendUserUid = MessageHelper.pareseVideoAndVoice$SwitchToVoiceAndVideo_from$a(msg);
        IVideoActivity currentVideoActivity = imc.getCurrentVideoChattingActivity();
        if (currentVideoActivity != null) {
            currentVideoActivity.switchToVoiceAndVideo(true);
        } else
            Log.d(TAG, "收到了来自" + friendUserUid + "的视频聊天：切换回音视频聊天模式.");
    }

    /**
     * 处理 processMT20_OF_VIDEO$VOICE$REQUEST_REJECT$TO$A 指令逻辑。
     *
     * @param context
     * @param msg
     * @param videoCallOutObserver
     */
    // TODO: 【考虑一下221116】参考实时视频和语音请求的处理方式，当APP处于后台时，是否需要给个系统Notification通知？
    public static void processMT20(Context context, String msg, Observer videoCallOutObserver){
        System.out.println("NTMD:收到拒绝了！！！！！！！！！！！！！！");

        String uid = MessageHelper.pareseVideoAndVoiceRequest$Reject_to$a(msg);
        // 通知观察者：对方B已经拒绝了视频聊天请求
        if(videoCallOutObserver != null) {
            // 简单传一个整型作为“同意”的标识常量
            videoCallOutObserver.update(null, 0);
        } else {
            // 边界问题处理：当用户B同意了视频聊天，但A这端却按了home键（或其它操作），则此次拒绝将无法即时反馈到
            // 发起端A这边的界面上？？！！A这端的这种异常动作应该要想办法处理？？？？？否则A可能不知道B已经拒绝了？？（搞个对话框来提示？）
            Log.d(TAG, "收到了来自"+uid+"的同意视频聊天请求，但videoCallOutObserver==null，无法进入视频聊天!");
        }
    }

    /**
     * 处理 processMT17_OF_VIDEO$VOICE$REQUEST_REQUESTING$FROM$A 指令逻辑。
     *
     * @param context
     * @param msg
     * @param viodeoChatRequestObserver
     */
    // TODO: 【考虑一下221116】参考实时视频和语音请求的处理方式，当APP处于后台时，是否需要给个系统Notification通知？
    public static void processMT17(Context context, String msg, Observer viodeoChatRequestObserver){
        IMClientManager imc = MyApplication.getInstance2().getIMClientManager();
        String friendUserUid = MessageHelper.pareseVideoAndVoiceRequest$Requestting_from$a(msg);
        if(friendUserUid != null) {
            boolean appInForeground = MyApplication.getInstance2().isAppInForeground();
            if(appInForeground) {
                if(viodeoChatRequestObserver !=null) {
                    Log.w(TAG, "！！！收到了好友的[实时视频]聊天请求，聊天观察者对象不为null，直接通过弹出ui界面通知用户了。");
                    viodeoChatRequestObserver.update(null, friendUserUid);
                    return;
                }
            }

            Log.w(TAG, "！！！收到了好友的[实时视频]聊天请求，但聊天观察者对象为null，只能用Notification的方式通知用户了（isAppInForeground="+appInForeground+"）！");
            RosterElementEntity friendRee = imc.getFriendsListProvider().getFriendInfoByUid2(friendUserUid);
            // 当聊天界面没有打开 且 不存在注册了observer的Activity处于前台时就发一个Notification吧
            NotificationPromptHelper.showVoiceAndVideoRequestNotivication(context, friendUserUid, friendRee != null?friendRee.getNickNameWithRemark() : "null("+friendUserUid+")");
        } else {
            Log.w(TAG, "收到了好友的[实时视频]聊天请求，但传过来的UID="+friendUserUid+",这中间肯定出错了！");
        }
    }

    /**
     * 处理 processMT18_OF_VIDEO$VOICE$REQUEST_ABRORT$FROM$A 指令逻辑。
     *
     * @param context
     * @param msg
     * @param videoCallComeObserver
     */
    // TODO: 【考虑一下221116】参考实时视频和语音请求的处理方式，当APP处于后台时，是否需要给个系统Notification通知？
    public static void processMT18(Context context, String msg, Observer videoCallComeObserver){
        String friendUserUid = MessageHelper.pareseVideoAndVoiceRequest$Abort_from$a(msg);
        // 通知观察者：对方A已经取消了视频聊天请求——本地用户当然就不用处理了（同意或拒绝自然就没意义了）
        if(videoCallComeObserver != null)
            videoCallComeObserver.update(null, friendUserUid);
        else {
            // 边界问题处理：当用户A取消了视频聊天，但B这端却按了home键（或其它操作），则此次取消将无法即时反馈到
            // B这边的界面上？？！！B这端的这种异常动作应该要想办法处理？？？？？否则B可能不知道A已经取消了？？（搞个对话框来提示？）
            Log.d(TAG, "收到了来自"+friendUserUid+"的取消视频聊天请求，但videoCallOutObserver==null，无法通知上层界面!");
        }
    }

    /**
     * 处理 processMT35_OF_REAL$TIME$VOICE_END$CHATTING 指令逻辑。
     *
     * @param context
     * @param msg
     */
    // TODO: 【考虑一下221116】参考实时视频和语音请求的处理方式，当APP处于后台时，是否需要给个系统Notification通知？
    public static void processMT35(Context context, String msg){
        IMClientManager imc = MyApplication.getInstance2().getIMClientManager();

        String friendUserUid = RealTimeVoiceMessageHelper.pareseRealTimeVoice$EndChatting_from$a(msg);
        IVideoActivity currentVideoActivity = imc.getCurrentVideoChattingActivity();
        if(currentVideoActivity != null) {
            currentVideoActivity.showHint(context.getString(R.string.partnet_exit_talk), true);
            currentVideoActivity.finish();
        } else
            Log.d(TAG, "收到了来自"+friendUserUid+"的实时语音聊天：结束本次实时语音聊天指令.");
    }

    /**
     * 处理 processMT34_OF_REAL$TIME$VOICE$REQUEST_REJECT$TO$A 指令逻辑。
     *
     * @param context
     * @param msg
     * @param realTimeVoiceCallOutObserver
     */
    // TODO: 【考虑一下221116】参考实时视频和语音请求的处理方式，当APP处于后台时，是否需要给个系统Notification通知？
    public static void processMT34(Context context, String msg, Observer realTimeVoiceCallOutObserver){
        String uid = RealTimeVoiceMessageHelper.pareseRealTimeVoiceRequest$Reject_to$a(msg);
        // 通知观察者：对方B已经拒绝了实时语音聊天请求
        if(realTimeVoiceCallOutObserver != null) {
            // 简单传一个整型作为“同意”的标识常量
            realTimeVoiceCallOutObserver.update(null, 0);
        } else {
            // TODO 边界问题处理：当用户B同意了实时语音聊天，但A这端却按了home键（或其它操作），则此次拒绝将无法即时反馈到
            // 发起端A这边的界面上？？！！A这端的这种异常动作应该要想办法处理？？？？？否则A可能不知道B已经拒绝了？？（搞个对话框来提示？）
            Log.d(TAG, "收到了来自"+uid+"的同意实时语音聊天请求" +"，但realTimeVoiceCallOutObserver==null，无法进入实时语音聊天!");
        }
    }

    /**
     * 处理 processMT31_OF_REAL$TIME$VOICE$REQUEST_REQUESTING$FROM$A 指令逻辑。
     *
     * @param context
     * @param msg
     * @param realTimeVoiceChatRequestObserver
     * @param realTimeVoiceChatRequestForNotIntChatUIObserver
     */
    public static void processMT31(Context context, String msg
            , Observer realTimeVoiceChatRequestObserver, Observer realTimeVoiceChatRequestForNotIntChatUIObserver){
        IMClientManager imc = MyApplication.getInstance2().getIMClientManager();

        String friendUserUid = RealTimeVoiceMessageHelper.pareseRealTimeVoiceRequest$Requestting_from$a(msg);
        if(friendUserUid != null) {
            boolean appInForeground = MyApplication.getInstance2().isAppInForeground();
            if(appInForeground) {
                // 【收到实时语音请求处理方式1：直接打开处理】
                if(realTimeVoiceChatRequestObserver !=null) {
                    Log.w(TAG, "！！收到了好友的[实时语音]聊天请求，聊天观察者对象不为null，直接通过弹出ui界面通知用户了。");
                    realTimeVoiceChatRequestObserver.update(null, friendUserUid);
                    return;
                }
                // 【收到实时语音请求处理方式2：间接打开处理】
                else if(realTimeVoiceChatRequestForNotIntChatUIObserver != null) {
                    Log.w(TAG, "！！收到了好友的[实时语音]聊天请求，聊天观察者对象不为null，直接通过弹出ui界面通知用户了。");
                    realTimeVoiceChatRequestForNotIntChatUIObserver.update(null, friendUserUid);
                    return;
                }
            }

            Log.w(TAG, "！！收到了好友的[实时语音]聊天请求，但聊天观察者对象为null，只能用Notification的方式通知用户了（isAppInForeground="+appInForeground+"）！");
            RosterElementEntity friendRee = imc.getFriendsListProvider().getFriendInfoByUid2(friendUserUid);
            // 当聊天界面没有打开 且 不存在注册了RealTimeVoiceChatRequestForNotIntChatUIObserver的Activity处于前台时就发一个Notification吧
            NotificationPromptHelper.showRealTimeVoiceRequestNotivication(context, friendUserUid, friendRee != null?friendRee.getNickNameWithRemark() : "null("+friendUserUid+")");
        } else {
            Log.w(TAG, "收到了好友的[实时语音]聊天请求，但传过来的UID="+friendUserUid+",这中间肯定出错了！");
        }
    }

    /**
     * 处理 processMT32_OF_REAL$TIME$VOICE$REQUEST_ABRORT$FROM$A 指令逻辑。
     *
     * @param context
     * @param msg
     * @param realTimeVoiceCallComeObserver
     */
    // TODO: 【考虑一下221116】参考实时视频和语音请求的处理方式，当APP处于后台时，是否需要给个系统Notification通知？
    public static void processMT32(Context context, String msg, Observer realTimeVoiceCallComeObserver){
        String friendUserUid = RealTimeVoiceMessageHelper.pareseRealTimeVoiceRequest$Abort_from$a(msg);
        // 通知观察者：对方A已经取消了实时语音聊天请求——本地用户当然就不用处理了（同意或拒绝自然就没意义了）
        if(realTimeVoiceCallComeObserver != null)
            realTimeVoiceCallComeObserver.update(null, friendUserUid);
        else {
            // TODO 边界问题处理：当用户A取消了实时语音聊天，但B这端却按了home键（或其它操作），则此次取消将无法即时反馈到
            // B这边的界面上？？！！B这端的这种异常动作应该要想办法处理？？？？？否则B可能不知道A已经取消了？？（搞个对话框来提示？）
            Log.d(TAG, "收到了来自"+friendUserUid+"的取消实时语音聊天请求，但realTimeVoiceCallComeObserver==null，无法通知上层界面!");
        }
    }

    /**
     * 处理 processMT03_OF_CHATTING_MESSAGE 指令逻辑。
     *
     * @param context
     * @param userid
     * @param fingerPrintOfProtocal
     * @param msg
     * @param receivedGiftObserverForGiftToolsUI
     * @param receivedGiftObserverForCommonUI
     */
    public static void processMT03(Context context, String userid, String fingerPrintOfProtocal, String msg
            , Observer receivedGiftObserverForGiftToolsUI, Observer receivedGiftObserverForCommonUI){
        final RosterElementEntity ree = MyApplication.getInstance2().getIMClientManager().getFriendsListProvider().getFriendInfoByUid2(userid);
        if(ree != null) {
            // ** 如果收到的是对方赠送的礼品，则要单独处理
            // 自v2.0后，普通的原始文本消息再也不是简单的纯文本了，而是MsgBodyRoot及其子类的JSON对象文件（此对象中的uid其实就是发送方的uid，目前主要用于服务端的离线消息存储时使用，客户端的话随便用）
            MsgBodyRoot tm = MsgBodyRoot.parseFromSender(msg);
            if(tm != null && tm.getTy() == MsgBody4Friend.TYPE_GIFT$SEND)
            {
                final String giftIdent = tm.getM(); // 聊天文本内容就是该礼品的ident

                // ** ==========【1】聊天消息列表里及时显示礼品的图标等
                // 礼品数据提交服务端处理成功后才真正地意味着礼品接收成功：马上在Ui上显示之！
                ChatDataHelper.addChatMessageData_incoming(context, fingerPrintOfProtocal, ree, msg, 0, true, true);
                // 礼品的额外处理逻辑
                ChatMessageEventProcessor.processMT03_OF_CHATTING_MESSAGE_ofGiftExtra(context, giftIdent, receivedGiftObserverForGiftToolsUI, receivedGiftObserverForCommonUI);
            }
            // ** 礼品之外的消息还是真常逻辑处理就行了！
            else {
                ChatDataHelper.addChatMessageData_incoming(context, fingerPrintOfProtocal, ree, msg, 0, true, true);
            }
        } else {
            Log.w(TAG, "来自userid=" +userid+"的一对一聊天消息虽收到，但此此人不在好友列表中，本条消息处将被忽略！");
        }
    }

    /**
     * 处理 MT03_OF_CHATTING_MESSAGE 指令中有关礼品消息类型的额外处理逻辑。
     */
    private static void processMT03_OF_CHATTING_MESSAGE_ofGiftExtra(Context context, String giftIdent
            , Observer receivedGiftObserverForGiftToolsUI, Observer receivedGiftObserverForCommonUI) {

        // 目前礼品处理逻辑是：在接收方收到礼品后，才将礼品发送的记录和数据（包括减发送者的积分）提交服务端处理，且处理成功
        // 才将礼品显示在接收人的ui上。目前存的情况是：中途在礼品的网络交互过程中出现任何不成功的情况，将无法通知发送方（发
        // 送方看到的却是发送成功了），目前没有更好地处理方法，暂时先这样！

//        // ** ==========【1】聊天消息列表里及时显示礼品的图标等
//        // 礼品数据提交服务端处理成功后才真正地意味着礼品接收成功：马上在Ui上显示之！
//        ChatDataHelper.addChatMessageData_incoming(context, fingerPrintOfProtocal, ree, msg, 0, true, true, addMessagesObserver);

        // ** ==========【2】在礼品工具面板相关ui上即时显示收到的礼品（比如我的背包的ui刷新等）
        final GiftInPackageMeta gim = GiftsProvider.getIntance().getGiftInPackageMeta(false, context);
        // 如果我的礼品包数据未载入则首先载入之
        if (!gim.isHasLoaded()) {
            Observer afterSucessObs = (observable, data) -> {// 我的礼品包数据载入成功观察者
                // 【1】要与【2】处的参数一致哦
                // 同时通知“收到礼品”观察者（以便即时刷新相关ui的展现）
                if (receivedGiftObserverForGiftToolsUI != null)
                    receivedGiftObserverForGiftToolsUI.update(observable, new Object[]{giftIdent, gim});
                if (receivedGiftObserverForCommonUI != null)
                    receivedGiftObserverForCommonUI.update(observable, new Object[]{giftIdent, gim});
            };

            // 无条件保我的背包数据被首先加载过
            new GiftsProvider.LoadGiftInPackageMetaAsyncTask(context)
                    .setShowProgress(false)
                    .execute(gim, afterSucessObs);
        }
        // 礼品包数据已经载入过则直接把该礼品叠加到现有的礼品上（存在则数字+1，否则新加一个礼品）
        else {
            Observer afterSucessObs = (observable, data) -> {
                //** 把数据刷新到UI上
                Gift g = ((GiftsMeta) data).getGift(giftIdent);
                // 叠加礼品到我的背包里
                gim.receivedNewGift(g);

                // 【2】要与【1】处的参数一致哦
                // 同时通知“收到礼品”观察者（以便即时刷新相关ui的展现）
                if (receivedGiftObserverForGiftToolsUI != null)
                    receivedGiftObserverForGiftToolsUI.update(observable, new Object[]{giftIdent, gim});
                if (receivedGiftObserverForCommonUI != null)
                    receivedGiftObserverForCommonUI.update(observable, new Object[]{giftIdent, gim});
            };

            // 因新加入礼品包时需要完整的gift对象，所以下代码无条件保证礼品基础数据被首先加载过。如果礼品基础数据还未加载过则先加载，如果已加载则直接使用。完成后才把该礼品加入到我的背包了
            new GiftsProvider.LoadGiftsMetaAsyncTask(context)
                    .setShowProgress(false)
                    .execute(GiftsProvider.getIntance().getGiftsMeta(false), afterSucessObs);
        }
    }
}
